package org.piggy.core.netty.processor;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.piggy.common.enums.ResponseCode;
import org.piggy.common.exception.BasicException;
import org.piggy.core.context.GatewayContext;
import org.piggy.core.netty.wrapper.HttpRequestWrapper;
import org.piggy.core.filter.FilterFactory;
import org.piggy.core.filter.GatewayFilterChain;
import org.piggy.core.filter.GatewayFilterChainFactory;
import org.piggy.core.utils.RequestUtil;
import org.piggy.core.utils.ResponseUtil;

@Slf4j
public class DefaultNettyProcessor implements NettyProcessor {

    private final FilterFactory filterFactory = GatewayFilterChainFactory.getInstance();

    @Override
    public void process(HttpRequestWrapper wrapper) {
        FullHttpRequest request = wrapper.getRequest();
        ChannelHandlerContext channelHandlerContext = wrapper.getChannelHandlerContext();
        try {
            GatewayContext gatewayContext = RequestUtil.routeGatewayContext(request, channelHandlerContext);
            if(gatewayContext == null){
                FullHttpResponse httpResponse = ResponseUtil.getHttpResponse(ResponseCode.PATH_NO_MATCHED);
                doWriteAndRelease(channelHandlerContext, request, httpResponse);
                return;
            }

            GatewayFilterChain gatewayFilterChain = filterFactory.buildFilterChain(gatewayContext);
            gatewayFilterChain.doFilter(gatewayContext);
        } catch (BasicException e) {
            log.error("process error -> ", e);
            FullHttpResponse httpResponse = ResponseUtil.getHttpResponse(e.getCode());
            doWriteAndRelease(channelHandlerContext, request, httpResponse);
        } catch (Exception e) {
            log.error("process error -> ", e);
            FullHttpResponse httpResponse = ResponseUtil.getHttpResponse(ResponseCode.INTERNAL_ERROR);
            doWriteAndRelease(channelHandlerContext, request, httpResponse);
        }
    }

    private void doWriteAndRelease(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        ReferenceCountUtil.release(request);
    }

}
